﻿#region 文件信息

/*----------------------------------------------------------------
//
// 文件名称：
// 文件功能描述：
// 设计要求：
//
// 文 件 名：    TreeBuilderBase
// 创建者：      杨程
// 创建日期：	    2022/11/21 16:35:52

//----------------------------------------------------------------*/

#endregion

namespace Vampirewal.Core.GeneralTree;

/// <summary>
/// 通用树构建基础
/// </summary>
public abstract class TreeBuilderBase<TNode> : ITreeBuilder, IDisposable
    where TNode : class, ITreeNode
{
    /// <summary>
    ///
    /// </summary>
    public TreeBuilderBase()
    {
        //构造函数
        BindingTreeItems = new ObservableCollection<GeneralTreeContainer<TNode>>();
        _sourceTreeNodes = new List<GeneralTreeContainer<TNode>>();
    }



    #region 属性

    /// <summary>
    /// 全选节点
    /// </summary>
    public bool? CheckedAllNodes { get; private set; }

    /// <summary>
    /// 是否单步加载模式
    /// </summary>
    public bool IsSingleLoadMode { get; protected set; }

    /// <summary>
    /// 绑定的值
    /// </summary>
    public ObservableCollection<GeneralTreeContainer<TNode>> BindingTreeItems { get; }

    /// <summary>
    /// 树节点元数据
    /// </summary>
    protected List<GeneralTreeContainer<TNode>> _sourceTreeNodes;

    /// <summary>
    /// 
    /// </summary>
    public event Action<object> Evt_SelectedNode;

    /// <summary>
    /// 
    /// </summary>
    public event Action<object[]> Evt_CheckNodes;

    /// <summary>
    /// 
    /// </summary>
    public void Dispose()
    {
    }

    #endregion

    #region 公共方法

    #region 可重载方法

    /// <summary>
    /// 添加新的树型数据
    /// </summary>
    /// <param name="target">要插入的目标节点</param>
    /// <param name="subItems">要插入的节点，可以包含子节点</param>
    /// <exception cref="ArgumentNullException"/>
    public virtual void AppendNodes(TNode target, params TNode[] subItems)
    {
        if (subItems == null || subItems.Length == 0)
            return;

        var disNodes = GetDisplayNodes();
        var targetNode = disNodes.FirstOrDefault(t => t.TreeNode.IsSameNode(target));
        var treeNodes = GeneralTreeItemsManager<TNode>.BuildTree(subItems);

        if (targetNode == null)
            throw new NullReferenceException("要插入的数据，在原数据树中未找到");
        else
        {
            //设置父节点，如果subItems是树，那么内部会设置其子节点的信息（包括父节点）
            targetNode.AppendSubNode(treeNodes);
        }
    }

    /// <summary>
    /// 添加新的顶节点
    /// </summary>
    /// <param name="rootNodes">要添加的顶节点</param>
    public virtual void AddRootNodes(params TNode[] rootNodes)
    {
        foreach (var item in rootNodes)
        {
            var node = new GeneralTreeContainer<TNode>(item);
            node.RefreshLevel(BindingTreeItems.Count + 1);
            BindingTreeItems.Add(node);
        }
    }

    /// <summary>
    /// 当前数据树中是否包含<paramref name="node"/>节点
    /// </summary>
    /// <param name="node">搜索的节点</param>
    /// <returns></returns>
    public virtual bool HasItem(TNode node)
    {
        var itemsSource = GetDisplayNodes();
        return itemsSource.Any(t => t.TreeNode.IsSameNode(node));
    }

    /// <summary>
    /// 获取树层级路径
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    public string GetTreeLevelPath(TNode node)
    {
        return GetDisplayNodes().FirstOrDefault(t => t.TreeNode.IsSameNode(node)).TreeLevelPath;
    }

    /// <summary>
    /// 获取当前数据树列表
    /// </summary>
    /// <returns></returns>
    public virtual List<TNode> GetSourceItems()
    {
        return GetDisplayNodes().Select(t => t.TreeNode).ToList();
    }

    /// <summary>
    /// 根据<paramref name="entryNode"/>获取其父节点列表，包含<paramref name="entryNode"/>节点
    /// </summary>
    /// <param name="entryNode">开始获取的入口节点</param>
    public virtual List<TNode> GetParentNodeList(TNode entryNode)
    {
        var disNodes = GetDisplayNodes();
        var tmpNode = disNodes.FirstOrDefault(t => t.TreeNode.IsSameNode(entryNode)) ?? throw new ArgumentNullException("查询节点entryNode未找到");
        List<TNode> parentList = new List<TNode>() { tmpNode.TreeNode };
        var parent = tmpNode.ParentNode;
        while (true)
        {
            if (parent == null)
                break;
            else
            {
                parentList.Add(parent.TreeNode);
                parent = parent.ParentNode;
            }
        }
        return parentList;
    }

    /// <summary>
    /// 根据<paramref name="treeNodeList"/>更新整棵树，
    /// 内部构建树会默认将<paramref name="treeNodeList"/>的顶节点作为当前的root节点
    /// 请确保<paramref name="treeNodeList"/>与已有树顶节点一致
    /// </summary>
    /// <param name="treeNodeList">必须为包含顶节点的树型数据</param>
    /// <exception cref="Exception"/>
    public virtual void UpdateTreeNodes(IEnumerable<TNode> treeNodeList)
    {
        var disNodes = GetDisplayNodes();
        var rootNode = GeneralTreeItemsManager<TNode>.GetRootNode(treeNodeList);                            //传入数据顶节点
        var treeNodes = GeneralTreeItemsManager<TNode>.BuildTree(treeNodeList);                             //传入数据构建树

        if (BindingTreeItems.Count != 0 && !rootNode.IsSameParent(BindingTreeItems.First().TreeNode))
            throw new Exception("传入树与已有树顶节点不相同");
        else
        {
            foreach (var rootItem in treeNodes)
            {
                if (disNodes.Any(t => t.TreeNode.IsSameNode(rootItem.TreeNode)))
                {
                    //在已有树中找到顶节点，则继续查询并更新下级子节点
                    //获取子节点
                    List<GeneralTreeContainer<TNode>> tmpSubNodes = new List<GeneralTreeContainer<TNode>>(rootItem.SubItems);

                    while (tmpSubNodes.Count > 0)
                    {
                        var nodesArray = tmpSubNodes.ToArray();
                        tmpSubNodes.Clear();
                        //查找并添加新节点
                        foreach (var item in nodesArray)
                        {
                            var parent = disNodes.FirstOrDefault(t => t.TreeNode.IsSubNode(item.TreeNode));
                            var loadedNode = disNodes.FirstOrDefault(t => t.TreeNode.IsSameNode(item.TreeNode));
                            if (loadedNode == null)
                            {
                                //未找到节点，表示为新增加节点
                                item.ParentNode = parent;
                                parent.SubItems.Add(item);
                            }
                            else if (item.SubItems != null)
                            {
                                //添加子节点
                                tmpSubNodes.AddRange(item.SubItems);
                            }
                        }
                    }
                }
                else
                {
                    //未在已有树中找到顶节点，则直接添加
                    BindingTreeItems.Add(rootItem);
                }
            }
        }
    }

    /// <summary>
    /// 删除已有节点
    /// </summary>
    /// <param name="isRemoveParentNode">当子节点清空后，是否删除父节点</param>
    /// <param name="delNodes">要删除的节点项</param>
    public virtual void RemoveNode(bool isRemoveParentNode, params TNode[] delNodes)
    {
        if (delNodes.Length < 1)
            return;
        List<TNode> delNodeList = delNodes.ToList();
        for (int i = 0; i < BindingTreeItems.Count; i++)
        {
            if (delNodeList.Count == 0)
                break;
            var tmpDelNode = delNodeList.FirstOrDefault(t => t.IsSameNode(BindingTreeItems[i].TreeNode));
            if (tmpDelNode != null)
            {
                BindingTreeItems.RemoveAt(i);
                delNodeList.Remove(tmpDelNode);
                i--;
            }
            else
            {
                BindingTreeItems[i].RemoveSubNode(isRemoveParentNode, ref delNodeList);
                if (isRemoveParentNode && BindingTreeItems[i].SubItems.Count < 1)
                {
                    BindingTreeItems.RemoveAt(i);
                    i--;
                }
            }
        }
    }

    /// <summary>
    /// 开始加载子项（通过ITreeBuilder接口调用）
    /// </summary>
    /// <param name="parentNode"></param>
    protected virtual void DoLoadSubItems(TNode parentNode)
    { }

    #endregion

    #region 获取显示节点

    /// <summary>
    /// 获取已显示的节点列表
    /// </summary>
    /// <param name="parentNodes">需要查询的树，null表示查询BindingTreeItems</param>
    /// <returns></returns>
    public List<GeneralTreeContainer<TNode>> GetDisplayNodes(params GeneralTreeContainer<TNode>[] parentNodes)
    {
        List<GeneralTreeContainer<TNode>> disNodes = new List<GeneralTreeContainer<TNode>>();
        if (parentNodes.Length == 0)
        {
            foreach (var item in BindingTreeItems)
            {
                disNodes.Add(item);
                item.GetSubNodeList(ref disNodes);
            }
        }
        else
        {
            foreach (var item in parentNodes)
            {
                disNodes.Add(item);
                item.GetSubNodeList(ref disNodes);
            }
        }
        return disNodes;
    }

    /// <summary>
    /// 获取Cheked条件的节点
    /// </summary>
    /// <param name="TrueOrFalse">筛选条件</param>
    /// <returns></returns>
    public List<TNode> GetCheckedNodes(bool? TrueOrFalse)
    {
        return GetDisplayNodes().Where(w => w.IsChecked == TrueOrFalse).Select(s => s.TreeNode).ToList();
    }

    #endregion

    #endregion

    #region 私有方法

    #region TreeGrid选中状态变更

    private void SetNodeCheckStateOfSubItem(GeneralTreeContainer<TNode> curItem, bool isChecked)
    {
        curItem.IsChecked = isChecked;
        if (curItem.SubItems != null)
            foreach (var item in curItem.SubItems)
            {
                SetNodeCheckStateOfSubItem(item, isChecked);
            }
    }

    private void SetNodeCheckStateOfParentItem(GeneralTreeContainer<TNode> curItem)
    {
        if (curItem.ParentNode != null)
        {
            var checkGroup = curItem.ParentNode.SubItems.GroupBy(t => t.IsChecked);
            curItem.ParentNode.IsChecked = checkGroup.Count() > 1 ? null : checkGroup.First().First().IsChecked;
            SetNodeCheckStateOfParentItem(curItem.ParentNode);
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="curNode"></param>
    /// <param name="isChecked"></param>
    public void CheckAndUpdateStateNode(object curNode, bool isChecked)
    {
        var disNodesList = GetDisplayNodes();
        var cur = disNodesList.FirstOrDefault(t => t.Equals(curNode));
        if (cur != null)
        {
            //刷新状态
            SetNodeCheckStateOfSubItem(cur, isChecked);
            SetNodeCheckStateOfParentItem(cur);

            //更新全部选中状态
            var group = disNodesList.GroupBy(t => t.IsChecked);
            CheckedAllNodes = group.Count() > 1 ? null : group.First().First().IsChecked;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="isChecked"></param>
    public void SetCheckStatusOfAllNodes(bool isChecked)
    {
        var disNodesList = GetDisplayNodes();
        disNodesList.ForEach(t => t.IsChecked = isChecked);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="selectedItem"></param>
    public void SelectedCopyItemByCtrl(object selectedItem)
    {
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="selectedItem"></param>
    public void SelectedCopyItemByShift(object selectedItem)
    {
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="selectedItem"></param>
    public void ClearSelectedCopyItems(object selectedItem)
    {
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="searchStr"></param>
    /// <param name="searchOfDown"></param>
    public void SearchAndLocationNode(string searchStr, bool searchOfDown = true)
    {
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="parentNode"></param>
    public void BeginLoadSubItems(object parentNode)
    {
        DoLoadSubItems(parentNode as TNode);
    }

    #endregion

    #endregion
}